﻿// -----------------------------------------------------------------------
// <copyright file="PngImageTest.cs">
// Copyright (c) 2013 Andrey Veselov. All rights reserved.
// License:  Microsoft Public License (MS-PL)
// Contacts: http://andrey.moveax.com  andrey@moveax.com
// </copyright>
// -----------------------------------------------------------------------

namespace PortableImagingLibrary.UnitTests
{
    using System;
    using System.IO;
    using System.Threading.Tasks;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using PortableImagingLibrary.Png;

    [TestClass]
    [DeploymentItem("Images\\" + PngImageTest._truecolorWithAlphaFileName, "Images")]
    [DeploymentItem("Images\\" + PngImageTest._truecolorFileName, "Images")]
    [DeploymentItem("Images\\" + PngImageTest._indexed256FileName, "Images")]
    [DeploymentItem("Images\\" + PngImageTest._indexed128FileName, "Images")]
    [DeploymentItem("Images\\" + PngImageTest._indexed016FileName, "Images")]
    public class PngImageTest
    {
        private const string _truecolorFileName = "Truecolor-3x3.png";
        private const string _truecolorWithAlphaFileName = "TruecolorWithAlpha-3x3.png";
        private const string _indexed256FileName = "Indexed256-3x3.png";
        private const string _indexed128FileName = "Indexed128WithTransparency-3x3.png";
        private const string _indexed016FileName = "Indexed016-3x3.png";

        [TestMethod]
        public void LoadTruecolorWithAlphaPngFile()
        {
            this.LoadFileTest(PngImageTest._truecolorWithAlphaFileName, this.TruecolorWithAlphaPngPixels());
        }

        [TestMethod]
        public void LoadTruecolorPngFile()
        {
            this.LoadFileTest(PngImageTest._truecolorFileName, this.TruecolorWithAlphaPngPixels(0xff));
        }

        [TestMethod]
        public void LoadTruecolorIndexedPngFile()
        {
            this.LoadFileTest(PngImageTest._indexed256FileName, this.TruecolorWithAlphaPngPixels(0xff));
            this.LoadFileTest(PngImageTest._indexed128FileName, this.TruecolorWithAlphaPngPixels(0x00));
            this.LoadFileTest(PngImageTest._indexed016FileName, this.TruecolorWithAlphaPngPixels(0xff));
        }

        private void LoadFileTest(string fileName, uint[] pixels)
        {
            // Arrange
            string pngFile = string.Format("{0}\\Images\\{1}", AppDomain.CurrentDomain.BaseDirectory, fileName);

            var image = new Image();

            // Act
            using (var pngStream = new FileStream(pngFile, FileMode.Open, FileAccess.Read)) {
                Task imgTask = image.LoadPngAsync(pngStream);
                imgTask.Wait();
            }

            // Assert
            Assert.IsNotNull(image.Pixels);
            Assert.AreEqual(pixels.Length, image.Pixels.Length);
            for (int i = 0; i < pixels.Length; i++)
                Assert.AreEqual(pixels[i], image.Pixels[i]);
        }

        [TestMethod]
        public void SaveTruecolorWithAlphaPngFile()
        {
            // Arrange
            string pngFile = string.Format("{0}\\Images\\{1}", AppDomain.CurrentDomain.BaseDirectory, PngImageTest._truecolorWithAlphaFileName);
            string tmpFile = string.Format("{0}\\Images\\{1}", AppDomain.CurrentDomain.BaseDirectory, "saveArgbTest.png");
            var pixels = this.TruecolorWithAlphaPngPixels();

            var image = new Image();

            // Act
            using (var pngStream = new FileStream(pngFile, FileMode.Open, FileAccess.Read)) {
                Task imgTask = image.LoadPngAsync(pngStream);
                imgTask.Wait();
            }

            using (var pngSteam = new FileStream(tmpFile, FileMode.Create, FileAccess.Write)) {
                Task imgTask = image.SavePngAsync(pngSteam, new PngSaveOptions() { BitDepth = 8, ColorType = ColorType.TruecolorWithAlpha });
                imgTask.Wait();
            }

            var image2 = new Image();

            using (var pngStream = new FileStream(tmpFile, FileMode.Open, FileAccess.Read)) {
                Task imgTask = image2.LoadPngAsync(pngStream);
                imgTask.Wait();
            }

            // Assert
            Assert.IsNotNull(image2.Pixels);
            Assert.AreEqual(pixels.Length, image2.Pixels.Length);
            for (int i = 0; i < pixels.Length; i++)
                Assert.AreEqual(pixels[i], image2.Pixels[i]);

        }

        [TestMethod]
        public void CreatePng()
        {
            // Arrange
            string tmpFile = string.Format("{0}\\Images\\{1}", AppDomain.CurrentDomain.BaseDirectory, "createArgbTest.png");

            const int width = 3;
            const int height = 3;
            var image = new Image(width, height);
            var pixels = this.TruecolorWithAlphaPngPixels();

            // Act 
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    var pos = x + y * width;
                    uint pixel = pixels[pos];
                    image.SetPixel(x, y, pixel);
                }
            }

            using (var pngSteam = new FileStream(tmpFile, FileMode.Create, FileAccess.Write)) {
                Task imgTask = image.SavePngAsync(pngSteam, new PngSaveOptions() { BitDepth = 8, ColorType = ColorType.TruecolorWithAlpha });
                imgTask.Wait();
            }

            var image2 = new Image();

            using (var pngStream = new FileStream(tmpFile, FileMode.Open, FileAccess.Read)) {
                Task imgTask = image2.LoadPngAsync(pngStream);
                imgTask.Wait();
            }

            // Assert
            Assert.IsNotNull(image2.Pixels);
            Assert.AreEqual(pixels.Length, image2.Pixels.Length);
            for (int i = 0; i < pixels.Length; i++)
                Assert.AreEqual(pixels[i], image2.Pixels[i]);
        }

        private uint[] TruecolorWithAlphaPngPixels(Nullable<byte> alpha = null)
        {
            byte alphaValue = alpha.HasValue ? alpha.Value : (byte)0x80;
            uint pixelValue = alphaValue != 0 ?
                this.ArgbToUInt32(alphaValue, 0x9c, 0x13, 0xb0) :
                0x0073bb43;

            return new uint[]
            {
                this.ArgbToUInt32(0xff, 0xf4, 0x0e, 0x0e),
                this.ArgbToUInt32(0xff, 0x48, 0x49, 0x4a),
                this.ArgbToUInt32(0xff, 0x33, 0x84, 0xc8),
                pixelValue,
                this.ArgbToUInt32(0xff, 0xff, 0xff, 0xff),
                this.ArgbToUInt32(0xff, 0xff, 0x96, 0x00),
                this.ArgbToUInt32(0xff, 0x00, 0x00, 0x00),
                this.ArgbToUInt32(0xff, 0x18, 0xe3, 0x2b),
                this.ArgbToUInt32(0xff, 0xa6, 0xa7, 0xa8)
            };
        }

        private uint ArgbToUInt32(byte a, byte r, byte g, byte b)
        {
            return (uint)(a << 24 | r << 16 | g << 8 | b);
        }
    }
}
